//#include "word_tree.h++"

#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <boost/foreach.hpp>
#include "word_tree.h++"
#include "mapped_file.h++"
#include <stdint.h>


std::string Document::get_path_string(const MappedMemory& m) {
	std::string name = m.get(filename).get_string();
	if (!pathbase.pointer.address)
		return name;

	boost::filesystem::path path(name);
	path = boost::filesystem::path(m.get(pathbase).get_string())/path;
	return path.string();
}

namespace WordTree {

PointerTo<DetailedDataHeader>& NodeHeader::get_detailed_data() {
	return *HashTable(*this).get_pointer_after<PointerTo<DetailedDataHeader> >();
}
HashTable NodeHeader::getHashTable() {
	return HashTable(*this);
}


NodeHeader* NodeHeader::getWord(MappedMemory& file, const std::string& str) {
	if (str.empty())
		return this;
	char first = *str.begin();
	std::string childstr = str.substr(1);
	pointer_to_us* childptr = getHashTable().get_entry(first);
	if (!childptr)
		return NULL;
	NodeHeader& child = file.get(*childptr);
	if (child.character != first)
		return NULL;
	return child.getWord(file, childstr);
}

bool HashTable::can_store_without_collision(const std::set<uint8_t>& chars) {
	std::vector<bool> used; // if your STL doesn't have a vector<bool>
	// partial templatization, ditch it.
	used.resize(header.hashtable_size, false);
	BOOST_FOREACH(uint8_t c, chars) {
		if (used[get_entry_index(c)])
			return false;
		used[get_entry_index(c)] = true;
	}
	return true;
}
void HashTable::optimize_node_header_for_chars(const std::set<uint8_t>& chars) {
	size_t min_length = chars.size();
	header.hashtable_size = min_length;
	if (!min_length) {
		header.hashtable_lower_bound = header.hashtable_factor = 0;
		return; // that was easy.
	}
	uint8_t min = *chars.begin();
	uint8_t max = *chars.rbegin();
	size_t max_length = 1+max - min;
	header.hashtable_lower_bound = min;
	uint8_t& hash_size = header.hashtable_size;
	uint8_t& hash_factor = header.hashtable_factor;
	std::vector<bool> used;
	for (hash_size = min_length; hash_size <= max_length; ++hash_size) {
		used.clear();
		used.resize(min_length, false);
		for (hash_factor = 0; hash_factor < hash_size; ++hash_factor) {
			if (can_store_without_collision(chars))
				return;
		}
	}
	throw std::logic_error("hashtable optimizing code has a bug : cannot find a suitable hashtable");
}



NodeHeader UnserializedNode::get_optimized_hashtable_parameter() const {
	NodeHeader ret;
	HashTable fake(ret);
	std::set<uint8_t> chars;
	BOOST_FOREACH(const map_type::value_type& e, childs) {
		chars.insert(e.first);
	}
	fake.optimize_node_header_for_chars(chars);
	ret.character = character;
	return ret;
}

}

std::string UnserializedDocument::get_dirpath() const {
	return relative_path.parent_path().string();
}

std::string UnserializedDocument::get_filename() const {
	return relative_path.filename().string();
}
UnserializedDocument::UnserializedDocument(const boost::filesystem::path& p)
		: relative_path(p) {}

namespace WordTree {

UnserializedNode& UnserializedNode::create_get_word(const std::string& given_str) {
	if (given_str.empty())
		return *this;
	char first_char = *given_str.begin();
	std::string str = given_str.substr(1);
	map_type::iterator iter = childs.find(first_char);
	if (iter == childs.end()) {
		boost::shared_ptr<UnserializedNode> ptr (new UnserializedNode(first_char));
		childs[first_char] = ptr;
		return ptr->create_get_word(str);
	}
	return iter->second->create_get_word(str);
}


void UnserializedNode::print_representation(std::ostream& o, size_t recursion) {
	std::string indentation(recursion, '\t');
	recursion++;
	BOOST_FOREACH(map_type::value_type& e, childs) {
		std::cout << indentation << "'" << e.first << "'\n";
		e.second->print_representation(o, recursion);
	}
	std::cout << std::flush;

}

}
